// Copyright (c) Microsoft Corporation. All rights reserved.

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Threading;
using System.Diagnostics;
using System.Xml.Serialization;
using System.Globalization;

using Microsoft.TeamFoundation.Migration.Toolkit.Wit;

namespace Microsoft.TeamFoundation.Migration.Toolkit
{
    /*
     * 
     * <EventSink provider="TextReport">
     *   <TextReport>
     *     <File>c:\temp\textreport.txt</File>
     *     <Append>false</Append>
     *   </TextReport>
     * </EventSink>
     *
     * 
     */

    /// <summary>
    /// The text file report class.
    /// </summary>
    [XmlType("TextReport")]
    public class TextReportAdaptor : IMigrationSessionEventSink
    {
        /// <summary>
        /// Creates a new text report adaptor
        /// </summary>
        public TextReportAdaptor()
        {
            m_processId = MigrationUtil.GetProcessId();
        }

        /// <summary>
        /// Gets file path that the report is writing to.
        /// </summary>
        public string File
        {
            get
            {
                return m_filePath;
            }
            set
            {
                m_filePath = value;
            }
        }

        /// <summary>
        /// Gets whether or not the report is being appended to or over-written.
        /// </summary>
        public bool Append
        {
            get
            {
                return m_append;
            }
            set
            {
                m_append = value;
            }
        }

        /// <summary>
        /// Registers a migration session with the report engine.
        /// </summary>
        /// <param name="session">The session to register with.</param>
        public virtual void RegisterSession(IMigrationSession session)
        {
            if (session == null)
            {
                throw new ArgumentNullException("session");
            }

            resetFile();

            session.SessionAborted += new EventHandler<MigrationSessionEventArgs>(SessionEvent);
            session.SessionComplete += new EventHandler<MigrationSessionEventArgs>(SessionEvent);
            session.SessionStart += new EventHandler<MigrationSessionEventArgs>(SessionEvent);
            session.SessionError += new EventHandler<MigrationSessionEventArgs>(SessionEvent);

            VersionControlSession vcSession = session as VersionControlSession;
            if (vcSession != null)
            {
                RegisterVCSession(vcSession);
            }

            WorkItemTrackingSession witSession = session as WorkItemTrackingSession;
            if (witSession != null)
            {
                registerWitSession(witSession);
            }
        }

        /*
         * Opens/Creates the text report output file based on the configuration values.
         */
        void resetFile()
        {
            if (string.IsNullOrEmpty(File))
            {
                throw new MigrationException(MigrationToolkitResources.FileElementEmpty);
            }

            lock (m_locker)
            {
                if (m_textWriter != null)
                {
                    throw new MigrationException(MigrationToolkitResources.OneSessionPerTextReport);
                }

                string parentPath = Path.GetDirectoryName(File);
                if (! string.IsNullOrEmpty(parentPath))
                {
                    Directory.CreateDirectory(parentPath);
                }

                m_textWriter = new StreamWriter(new FileStream(
                    File,
                    (Append) ? FileMode.Append : FileMode.Create,
                    FileAccess.Write,
                    FileShare.Read));

                m_textWriter.AutoFlush = true;
            }
        }

        /*
         * registers a VC session with the report.
         */
        protected virtual void RegisterVCSession(VersionControlSession vcSession)
        {
            vcSession.AnalysisAborted += new EventHandler<VersionControlEventArgs>(SessionEvent);
            vcSession.AnalysisComplete += new EventHandler<VersionControlEventArgs>(SessionEvent);
            vcSession.AnalysisError += new EventHandler<VersionControlEventArgs>(SessionEvent);
            vcSession.AnalysisWarning += new EventHandler<VersionControlEventArgs>(SessionEvent);
            vcSession.AnalyzingChangeComplete += new EventHandler<VersionControlEventArgs>(SessionEvent);
            vcSession.AnalyzingChangeStarting += new EventHandler<VersionControlEventArgs>(SessionEvent);
            vcSession.AnalysisStarting += new EventHandler<VersionControlEventArgs>(SessionEvent);
            vcSession.MigrationStarting += new EventHandler<VersionControlEventArgs>(SessionEvent);
            vcSession.MigratingChangeComplete += new EventHandler<VersionControlEventArgs>(SessionEvent);
            vcSession.MigratingChangeStarting += new EventHandler<VersionControlEventArgs>(SessionEvent);
            vcSession.MigrationAborted += new EventHandler<VersionControlEventArgs>(SessionEvent);
            vcSession.MigrationComplete += new EventHandler<VersionControlEventArgs>(SessionEvent);
            vcSession.MigrationError += new EventHandler<VersionControlEventArgs>(SessionEvent);
            vcSession.MigrationDownloadStarted += new EventHandler<VersionControlEventArgs>(SessionEvent);
            vcSession.MigrationDownloadCompleted += new EventHandler<VersionControlEventArgs>(SessionEvent);
            vcSession.MigrationWarning += new EventHandler<VersionControlEventArgs>(SessionEvent);
        }

        private void registerWitSession(WorkItemTrackingSession witSession)
        {
            witSession.WorkItemMigrationStart += new EventHandler<WorkItemMigrationEventArgs>(SessionEvent);
            witSession.WorkItemMigrationComplete += new EventHandler<MigrationCompleteEventArgs>(SessionEvent);
            witSession.WorkItemAnalysisComplete += new EventHandler<AnalysisCompleteEventArgs>(SessionEvent);
            witSession.WorkItemMigrationFailed += new EventHandler<WorkItemMigrationEventArgs>(SessionEvent);
            witSession.WorkItemFieldConflict += new EventHandler<FieldConflictEventArgs>(SessionEvent);
        }

        #region Session level event handlers

        private void SessionEvent(object sender, MigrationEventArgs e)
        {
            string msg = string.Format(
                CultureInfo.InvariantCulture,
                "[{0}, {1}, {2}, {3}] {4}",            // Do not localize!
                m_processId,
                Thread.CurrentThread.ManagedThreadId,
                e.Time.ToString(CultureInfo.CurrentCulture),
                sender.ToString(),
                e.Description);

            if (e.Exception != null)
            {
                msg = string.Format(
                    CultureInfo.InvariantCulture,
                    "{0}, Exception: {1}",
                    msg,
                    e.Exception.ToString());
            }

            WriteLine(msg);
        }

        #endregion

        protected void WriteLine(string message)
        {
            lock (m_locker)
            {
                if (m_textWriter != null)
                {
                    m_textWriter.WriteLine(message);
                }
            }
        }

        #region IDisposable Members

        protected virtual void Dispose(bool disposing)
        {
            if (!disposed)
            {
                if (disposing)
                {
                    if (m_textWriter != null)
                    {
                        m_textWriter.Dispose();
                        m_textWriter = null;
                    }
                }

                // release unmanged resources
            }

            disposed = true;
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        #endregion

        StreamWriter m_textWriter;

        // serializes access to the adaptor for threaded access
        object m_locker = new object();

        // cache the current process Id
        int m_processId;

        bool m_append;
        string m_filePath;
        bool disposed;
    }
}
